//数组是引用类型

//汉塔诺问题解释：
//问题规模很大很复杂
import java.util.Scanner;

public class TestDemo {
    /**
     * @param pos1 起始位置
     * @param pos2 目标位置
     */
    public static void move(char pos1, char pos2) {
        System.out.print(pos1 + " -> " + pos2 + " ");
    }

    /**
     * @param n    数量个数
     * @param pos1 起始位置
     * @param pos2 中转位置
     * @param pos3 目标位置
     */
    public static void hanIo(int n, char pos1, char pos2, char pos3) {
        if (n == 1) {
            move(pos1, pos3);
            return;
        }
        hanIo(n - 1, pos1, pos3, pos2);
        move(pos1, pos3);
        hanIo(n - 1, pos2, pos1, pos3);
    }

    public static void main1(String[] args) {
        hanIo(1, 'A', 'B', 'C');
        System.out.println();
        hanIo(2, 'A', 'B', 'C');
        System.out.println();
        hanIo(3, 'A', 'B', 'C');
    }

    //数组是引用类型
    public static void main2(String[] args) {
        int[] array = new int[]{1, 2, 3, 4};
        int[] array2 = array ;
        //array2这个引用指向了array这个引用所指向的对象
        array2[0] = 99;//所以两者的地址是同一个，那么改变了谁，两者都会发生了变化
        //在main方法中：
        //int[] array = new int[]{1,2,3,4};
        //虚拟机栈：存入了一个array的数组
        //然而堆中存入的是array的中的数字或者字符等，这是对象
        //在虚拟机中的array是在方法中，所以通过地址来指向堆中的对象，在方法中的array是存入的是地址

        //结论：array这个引用变量指向了数组对象
        //array2被赋值了array的变量 所以array2也通过引用变量指向了array中的数组的对象
        //所以这个时候就是两个引用变量指向同一个对象
        //所以两者的地址是同一个，那么改变了谁，两者都会发生了变化
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
        System.out.println();

        for (int c : array) {
            System.out.print(c + " ");
        }
        System.out.println();
    }

//    引用类型
//    什么叫引用类型 在这个变量中，储存的是"地址”
//    JVM将内存划分几个区
//    1.方法区，2.虚拟机区，3.本地方法栈区，4.堆，5.程序计数器
//    在Java SE 中我们需要重点了解堆和虚拟机区
//    平时所说的栈，其实指的就是Java虚拟机栈
//    如：局部变量存在栈里
//    堆：一般用来存储对象的
//    本地方法栈一般使用C语言和C++来写的


//    堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} )，
//    堆是随着程序开始运行时而创建，随着程序的退出而销毁，堆中的数据只要还有在使用，就不会被销毁。

//    每一块内存都有自己的使命
//    堆和方法区是所有线程共享的数据区 本地方法栈和虚拟机区以及程序计数器是处于线程隔离区

    public static void func() {
        int[] array1 = new int[3];
        array1[0] = 10;
        array1[1] = 20;
        array1[2] = 30;

        int[] array2 = new int[]{1,2,3,4,5};
        array2[0] = 100;
        array2[1] = 200;

        array1 = array2;
        array1[2] = 300;
        array1[3] = 400;
        array2[4] = 500;
        //再谈引用变量
        /**
         *首先这个数组1，在main方法中，先指向了堆中的数组1的对象，分别是10，20，30
         * 假设这个对象的地址是0x18，虚拟机上的main方法栈中保存了array1这些地址，array2定义为了1，2，3，4，5；
         * 这时候堆中array2的对象的值1，2，3，4，5；但是又对array2[0]和[1]修改，所以这时候堆中array2的对象的值变成了100，200，3，4，5
         * 再通过了array1=array2赋值，假设array2的地址是0x78，
         * 所以虚拟机中的main方法栈中两者的保留的地址均是0x78
         * 所以这个时候array2的对象中的值就会变成了100，200，300，400，500
         * 所以这两个引用均同时指向了一个对象，所以不管通过哪个引用，都可以把数组对象当中的值进行修改，
         * 那么array1的对象10，20，30，没有人再指向它了，那么JVM会自动把它回收掉
         * 当func方法执行完毕，此时array1和array2是局部变量，此时就会被回收，
         * 那么array2所指向的对象没有其它引用所指向的，所以它也会被JVM而自动回收掉
         */
    }

    //null 在 Java 中表示 "空引用" , 也就是一个不指向对象的引用.
    public static void main3(String[] args) {//null
        //int[] array = 0;
        //通常把数组直接赋值为0是错误的
        //因为它指的是引用类型，而零是整数类型，两者是不同的
        int[] array3 = null;
        //array3这个引用不指向任何对象
        System.out.println(array3.length);
        //这个时候会发生报错，因为array3不指向任何数组对象，所以也就无法就计算长度
        //这个时候就会出现空指针异常的报错提醒：NullPointerException
        //这个异常会伴随你的一生在我们学习过程中
        //如果以后遇到空指针异常那么我们需要定位，看看那个引用是空的
    }

}